/*
 * TileReader.cpp
 *
 *  Created on: 2009/01/29
 *      Author: Yongshik Cho
 */

#include "TileReader.h"
#include "../TextureManager.h"
#include "CollisionReader.h"
#include "TweenReader.h"
#include "AnimationReader.h"
#include "../core/Rect2D.h"
#include "../ScriptManager.h"
#include "../SceneManager.h"
#include "sqplus/sqplus.h"


using namespace SqPlus;


const std::string TileReader::_NAME_TAG					= "name";
const std::string TileReader::_PATH_TAG					= "path";
const std::string TileReader::_X_COUNT_TAG				= "xCount";
const std::string TileReader::_Y_COUNT_TAG				= "yCount";
const std::string TileReader::_SAME_ANIMATION_TILE_TAG	= "SameAnimationTile";
const std::string TileReader::_SAME_TWEEN_TILE_TAG		= "SameTweenTile";
const std::string TileReader::_TRANSPARENT_COLOR_TAG	= "TransparentColor";
const std::string TileReader::_BLENDTYPE_TAG			= "BlendType";
const std::string TileReader::_SORT_TAG					= "Sort";
const std::string TileReader::_ORIGIN_TAG				= "Origin";
const std::string TileReader::_UP_TAG					= "Up";

const std::string TileReader::_CENTERTYPE_TAG			= "CenterType";
const std::string TileReader::_FLIPTYPE_TAG				= "FlipType";
const std::string TileReader::_COLLIDETYPE_TAG			= "CollideType";
const std::string TileReader::_BLENDCOLOR_TAG			= "BlendColor";
const std::string TileReader::_LOCKED_TAG				= "Locked";
const std::string TileReader::_VISIBLE_RECT_TAG			= "VisibleRect";
const std::string TileReader::_VISIBLE_CENTER_TAG		= "VisibleCenter";
const std::string TileReader::_VISIBLE_TAG				= "Visible";

const std::string TileReader::_CENTER_LT_TAG			= "CENTER_LT";
const std::string TileReader::_CENTER_CT_TAG			= "CENTER_CT";
const std::string TileReader::_CENTER_RT_TAG			= "CENTER_RT";
const std::string TileReader::_CENTER_LM_TAG			= "CENTER_LM";
const std::string TileReader::_CENTER_CM_TAG			= "CENTER_CM";
const std::string TileReader::_CENTER_RM_TAG			= "CENTER_RM";
const std::string TileReader::_CENTER_LB_TAG			= "CENTER_LB";
const std::string TileReader::_CENTER_CB_TAG			= "CENTER_CB";
const std::string TileReader::_CENTER_RB_TAG			= "CENTER_RB";

const std::string TileReader::_FLIP_NONE_TAG			= "FLIP_NONE";
const std::string TileReader::_FLIP_LEFTRIGHT_TAG		= "FLIP_LEFTRIGHT";
const std::string TileReader::_FLIP_TOPBOTTOM_TAG		= "FLIP_TOPBOTTOM";

const std::string TileReader::_COLLIDABLE_PIXEL_TAG		= "COLLIDABLE_PIXEL";
const std::string TileReader::_COLLIDABLE_LINE_TAG		= "COLLIDABLE_LINE";
const std::string TileReader::_COLLIDABLE_TRIANGLE_TAG	="COLLIDABLE_TRIANGLE";
const std::string TileReader::_COLLIDABLE_RECT_TAG		= "COLLIDABLE_RECT";
const std::string TileReader::_COLLIDABLE_POLYGON_TAG	= "COLLIDABLE_POLYGON";

const std::string TileReader::_TRUE_TAG					= "true";
const std::string TileReader::_FALSE_TAG				= "false";

const std::string TileReader::_TWEEN_TAG				= "Tween";
const std::string TileReader::_POSITION_TAG				= "Position";
const std::string TileReader::_ROTATION_TAG				= "Rotation";
const std::string TileReader::_ANGLE_TAG				= "angle";
const std::string TileReader::_SCALE_TAG				= "Scale";
const std::string TileReader::_ANIMATION_TAG			= "Animation";
const std::string TileReader::_COLLISION_TAG			= "AltoCollision";
const std::string TileReader::_X_TAG					= "x";
const std::string TileReader::_Y_TAG					= "y";

const std::string TileReader::_SCRIPT_FILE_TAG			= "ScriptFile";
const std::string TileReader::_ON_LOAD_TAG				= "onLoad";

const std::string TileReader::_ATTACHMENT_TAG			= "Attachment";

const std::string TileReader::_BLEND_NONE_TAG			= "BLEND_NONE";
const std::string TileReader::_BLEND_ALPHA_TAG			= "BLEND_ALPHA";
const std::string TileReader::_BLEND_ADD_TAG			= "BLEND_ADD";
const std::string TileReader::_BLEND_SUB_TAG			= "BLEND_SUB";
const std::string TileReader::_BLEND_DARKEN_TAG			= "BLEND_DARKEN";
const std::string TileReader::_BLEND_LIGHTEN_TAG		= "BLEND_LIGHTEN";
const std::string TileReader::_BLEND_MULTIPLY_TAG		= "BLEND_MULTIPLY";
const std::string TileReader::_BLEND_SCREEN_TAG			= "BLEND_SCREEN";
const std::string TileReader::_BLEND_DIFFERENCE_TAG		= "BLEND_DIFFERENCE";
const std::string TileReader::_BLEND_INVERSE_DIFFERENCE_TAG		= "BLEND_INVERSE_DIFFERENCE";
const std::string TileReader::_BLEND_INVERT_TAG			= "BLEND_INVERT";
const std::string TileReader::_BLEND_DODGE_TAG			= "BLEND_DODGE";


Tile* TileReader::parse(TiXmlElement* element, RenderObjectContainer* roc)
{
	//read information about tile
	/*
	<Tile path='PC_M000.png' xCount=4 yCount=9 TransparentColor=0xFF
	CenterType='CENTER_LT' FlipType='FLIP_NONE' CollideType='COLLIDE_RECT'
	BlendColor=0xFFFFFFFF Locked='true' Visible='true'>
	<Position x=0 y=0/>
	<Rotation angle=0/>
	<Scale x=2 y=2/>
	*/
	int32 xCount = 0, yCount = 0, sameAnimationTile = -1, sameTweenTile = -1;
	bool useTrans = false;
	uint32 color32, blendColor;
	Color color;
	char *sCenterType, *sFlipType, *sCollideType, *sLocked, *sVisible, *sBlendColor, *sVisibleRect, *sVisibleCenter;

	char* sName = (char*)element->Attribute(_NAME_TAG.c_str());
	char* path = (char*)element->Attribute(_PATH_TAG.c_str());
	element->Attribute(_X_COUNT_TAG.c_str(), &xCount);
	element->Attribute(_Y_COUNT_TAG.c_str(), &yCount);

	TiXmlElement* node = element->FirstChildElement(_NAME_TAG.c_str()); if(node) sName = (char*)node->GetText();
	node = element->FirstChildElement(_PATH_TAG.c_str()); if(node) path = (char*)node->GetText();
	node = element->FirstChildElement(_X_COUNT_TAG.c_str()); if(node) xCount = atoi(node->GetText());
	node = element->FirstChildElement(_Y_COUNT_TAG.c_str()); if(node) yCount = atoi(node->GetText());

	if(!path || !xCount || !yCount)
		return 0;

	TCHAR wstr[512]; memset(wstr, 0, sizeof(wstr));
#ifdef UNICODE
	::MultiByteToWideChar(CP_UTF8, 0, path, (int32)strlen(path), wstr, 512);
	Texture* texture = TextureManager::GetInstance().load(wstr);
#else
	Texture* texture = TextureManager::GetInstance().load(path);
#endif
	if(!texture)
		return 0;


	Tile* tile = new Tile(texture, xCount, yCount);
	if(!tile)
		AltoThrow(Exception::ERR_MEMORY);

	if(sName)
#ifdef UNICODE
	{
		memset(wstr, 0, sizeof(wstr));
		::MultiByteToWideChar(CP_UTF8, 0, sName, (int32)strlen(sName), wstr, 512);
		tile->setName(wstr);
	}
#else
		tile->setName(sName);
#endif

	if(!element->Attribute(_SAME_ANIMATION_TILE_TAG.c_str(), &sameAnimationTile)) sameAnimationTile = -1;
	if(!element->Attribute(_SAME_TWEEN_TILE_TAG.c_str(), &sameTweenTile)) sameTweenTile = -1;
	char* transColor = (char*)element->Attribute(_TRANSPARENT_COLOR_TAG.c_str());
	if(transColor)
	{
		useTrans = true;
	}
	sCenterType = (char*)element->Attribute(_CENTERTYPE_TAG.c_str());
	sFlipType = (char*)element->Attribute(_FLIPTYPE_TAG.c_str());
	sCollideType = (char*)element->Attribute(_COLLIDETYPE_TAG.c_str());
	sBlendColor = (char*)element->Attribute(_BLENDCOLOR_TAG.c_str());
	sLocked = (char*)element->Attribute(_LOCKED_TAG.c_str());
	sVisibleRect = (char*)element->Attribute(_VISIBLE_RECT_TAG.c_str());
	sVisibleCenter = (char*)element->Attribute(_VISIBLE_CENTER_TAG.c_str());
	sVisible = (char*)element->Attribute(_VISIBLE_TAG.c_str());
	char* sBlendType = (char*)element->Attribute(_BLENDTYPE_TAG.c_str());
	char* sSort = (char*)element->Attribute(_SORT_TAG.c_str());


	node = element->FirstChildElement(_SAME_ANIMATION_TILE_TAG.c_str()); if(node) sameAnimationTile = atoi(node->GetText());
	node = element->FirstChildElement(_SAME_TWEEN_TILE_TAG.c_str()); if(node) sameTweenTile = atoi(node->GetText());
	node = element->FirstChildElement(_TRANSPARENT_COLOR_TAG.c_str()); if(node) { transColor = (char*)node->GetText(); useTrans = true; }
	node = element->FirstChildElement(_CENTERTYPE_TAG.c_str()); if(node) sCenterType = (char*)node->GetText();
	node = element->FirstChildElement(_FLIPTYPE_TAG.c_str()); if(node) sFlipType = (char*)node->GetText();
	node = element->FirstChildElement(_COLLIDETYPE_TAG.c_str()); if(node) sCollideType = (char*)node->GetText();
	node = element->FirstChildElement(_BLENDCOLOR_TAG.c_str()); if(node) sBlendColor = (char*)node->GetText();
	node = element->FirstChildElement(_LOCKED_TAG.c_str()); if(node) sLocked = (char*)node->GetText();
	node = element->FirstChildElement(_VISIBLE_RECT_TAG.c_str()); if(node) sVisibleRect = (char*)node->GetText();
	node = element->FirstChildElement(_VISIBLE_CENTER_TAG.c_str()); if(node) sVisibleCenter = (char*)node->GetText();
	node = element->FirstChildElement(_VISIBLE_TAG.c_str()); if(node) sVisible = (char*)node->GetText();
	node = element->FirstChildElement(_BLENDTYPE_TAG.c_str()); if(node) sBlendType = (char*)node->GetText();
	node = element->FirstChildElement(_SORT_TAG.c_str()); if(node) sSort = (char*)node->GetText();

	if(useTrans)
	{
		sscanf(transColor, "%x", &color32);
		color = Color::fromRGB((float)((color32>>16)&0xFF)/255.f, (float)((color32>>8)&0xFF)/255.f, (float)((color32>>0)&0xFF)/255.f);
		texture->setTransparentColor(color);
	}

	if(sCenterType)
	{
		if(sCenterType == _CENTER_LT_TAG)
			tile->setCenterType(Rect2D::CENTER_LT);
		else if(sCenterType == _CENTER_CT_TAG)
			tile->setCenterType(Rect2D::CENTER_CT);
		else if(sCenterType == _CENTER_RT_TAG)
			tile->setCenterType(Rect2D::CENTER_RT);
		else if(sCenterType == _CENTER_LM_TAG)
			tile->setCenterType(Rect2D::CENTER_LM);
		else if(sCenterType == _CENTER_CM_TAG)
			tile->setCenterType(Rect2D::CENTER_CM);
		else if(sCenterType == _CENTER_RM_TAG)
			tile->setCenterType(Rect2D::CENTER_RM);
		else if(sCenterType == _CENTER_LB_TAG)
			tile->setCenterType(Rect2D::CENTER_LB);
		else if(sCenterType == _CENTER_CB_TAG)
			tile->setCenterType(Rect2D::CENTER_CB);
		else if(sCenterType == _CENTER_RB_TAG)
			tile->setCenterType(Rect2D::CENTER_RB);
	}

	if(sFlipType)
	{
		if(sFlipType == _FLIP_NONE_TAG)
			tile->setFlipType(Renderable::FLIP_NONE);
		else if(sFlipType == _FLIP_LEFTRIGHT_TAG)
			tile->setFlipType(Renderable::FLIP_LEFT_RIGHT);
		else if(sFlipType == _FLIP_TOPBOTTOM_TAG)
			tile->setFlipType(Renderable::FLIP_TOP_BOTTOM);
	}

	if(sCollideType)
	{
		if(sCollideType == _COLLIDABLE_PIXEL_TAG)
			tile->setCollideType(Collidable::COLLIDABLE_PIXEL);
		else if(sCollideType == _COLLIDABLE_LINE_TAG)
			tile->setCollideType(Collidable::COLLIDABLE_LINE);
		else if(sCollideType == _COLLIDABLE_TRIANGLE_TAG)
			tile->setCollideType(Collidable::COLLIDABLE_TRIANGLE);
		else if(sCollideType == _COLLIDABLE_RECT_TAG)
			tile->setCollideType(Collidable::COLLIDABLE_RECT);
		else if(sCollideType == _COLLIDABLE_POLYGON_TAG)
			tile->setCollideType(Collidable::COLLIDABLE_POLYGON);
	}

	if(sBlendColor)
	{
		sscanf(sBlendColor, "%x", &blendColor);
		color = Color::fromARGB((float)((blendColor>>24)&0xFF)/255.f, (float)((blendColor>>16)&0xFF)/255.f,
				(float)((blendColor>>8)&0xFF)/255.f, (float)((blendColor>>0)&0xFF)/255.f);
		tile->setBlendColor(color);
	}

	if(sLocked)
	{
		if(sLocked == _TRUE_TAG)
			tile->setLocked(true);
		else if(sLocked == _FALSE_TAG)
			tile->setLocked(false);
	}

	if(sVisibleRect)
	{
		if(sVisibleRect == _TRUE_TAG)
			tile->setVisibleRect(true);
		else if(sVisibleRect == _FALSE_TAG)
			tile->setVisibleRect(false);
	}

	if(sVisibleCenter)
	{
		if(sVisibleCenter == _TRUE_TAG)
			tile->setVisibleCenter(true);
		else if(sVisibleCenter == _FALSE_TAG)
			tile->setVisibleCenter(false);
	}

	if(sVisible)
	{
		if(sVisible == _TRUE_TAG)
			tile->setVisible(true);
		else if(sVisible == _FALSE_TAG)
			tile->setVisible(false);
	}

	if(sSort)
	{
		if(sSort == _TRUE_TAG)
			tile->setSort(true);
		else if(sSort == _FALSE_TAG)
			tile->setSort(false);
	}

	if(sBlendType)
	{
		if(sBlendType == _BLEND_NONE_TAG)
			tile->setBlendType(Color::BLEND_NONE);
		else if(sBlendType == _BLEND_ALPHA_TAG)
			tile->setBlendType(Color::BLEND_ALPHA);
		else if(sBlendType == _BLEND_ADD_TAG)
			tile->setBlendType(Color::BLEND_ADD);
		else if(sBlendType == _BLEND_SUB_TAG)
			tile->setBlendType(Color::BLEND_SUB);
		else if(sBlendType == _BLEND_DARKEN_TAG)
			tile->setBlendType(Color::BLEND_DARKEN);
		else if(sBlendType == _BLEND_LIGHTEN_TAG)
			tile->setBlendType(Color::BLEND_LIGHTEN);
		else if(sBlendType == _BLEND_MULTIPLY_TAG)
			tile->setBlendType(Color::BLEND_MULTIPLY);
		else if(sBlendType == _BLEND_SCREEN_TAG)
			tile->setBlendType(Color::BLEND_SCREEN);
		else if(sBlendType == _BLEND_DIFFERENCE_TAG)
			tile->setBlendType(Color::BLEND_DIFFERENCE);
		else if(sBlendType == _BLEND_INVERSE_DIFFERENCE_TAG)
			tile->setBlendType(Color::BLEND_INVERSE_DIFFERENCE);
		else if(sBlendType == _BLEND_INVERT_TAG)
			tile->setBlendType(Color::BLEND_INVERT);
		else if(sBlendType == _BLEND_DODGE_TAG)
			tile->setBlendType(Color::BLEND_DODGE);
	}

	{
		for(TiXmlElement* attach=element->FirstChildElement(_ATTACHMENT_TAG.c_str());
		attach;attach=attach->NextSiblingElement(_ATTACHMENT_TAG.c_str()))
		{
			parseAttachment(attach, (Renderable*)tile);
		}

		for(TiXmlElement* collision=element->FirstChildElement(_COLLISION_TAG.c_str());
			collision;collision=collision->NextSiblingElement(_COLLISION_TAG.c_str()))
		{
			Collidable* collidable = CollisionReader::parse(collision);
			if(collidable)
				tile->addCollidable(collidable);
		}

		int32 x=0, y=0;
		double sx=1.f, sy=1.f, angle=0.f;

		TiXmlElement* origin = element->FirstChildElement(_ORIGIN_TAG.c_str());
		if(origin)
		{
			origin->Attribute(_X_TAG.c_str(), &x);
			node = origin->FirstChildElement(_X_TAG.c_str()); if(node) x = atoi(node->GetText());
			origin->Attribute(_Y_TAG.c_str(), &y);
			node = origin->FirstChildElement(_Y_TAG.c_str()); if(node) y = atoi(node->GetText());

			tile->setOrigin(Vector2D((float)x, (float)y));
		}

		TiXmlElement* up = element->FirstChildElement(_UP_TAG.c_str());
		if(up)
		{
			up->Attribute(_X_TAG.c_str(), &x);
			node = up->FirstChildElement(_X_TAG.c_str()); if(node) x = atoi(node->GetText());
			up->Attribute(_Y_TAG.c_str(), &y);
			node = up->FirstChildElement(_Y_TAG.c_str()); if(node) y = atoi(node->GetText());

			tile->setUp(Vector2D((float)x, (float)y));
		}

		TiXmlElement* position = element->FirstChildElement(_POSITION_TAG.c_str());
		if(position)
		{
			position->Attribute(_X_TAG.c_str(), &x);
			node = position->FirstChildElement(_X_TAG.c_str()); if(node) x = atoi(node->GetText());
			position->Attribute(_Y_TAG.c_str(), &y);
			node = position->FirstChildElement(_Y_TAG.c_str()); if(node) y = atoi(node->GetText());

			tile->setPosition(Vector2D((float)x, (float)y));
		}

		TiXmlElement* rotation = element->FirstChildElement(_ROTATION_TAG.c_str());
		if(rotation)
		{
			rotation->Attribute(_ANGLE_TAG.c_str(), &angle);
			node = rotation->FirstChildElement(_ANGLE_TAG.c_str()); if(node) angle = atof(node->GetText());

			tile->setRotation((float)angle);
		}

		TiXmlElement* scale = element->FirstChildElement(_SCALE_TAG.c_str());
		if(scale)
		{
			scale->Attribute(_X_TAG.c_str(), &sx);
			node = scale->FirstChildElement(_X_TAG.c_str()); if(node) sx = atof(node->GetText());
			scale->Attribute(_Y_TAG.c_str(), &sy);
			node = scale->FirstChildElement(_Y_TAG.c_str()); if(node) sy = atof(node->GetText());

			tile->setScale(Vector2D((float)sx, (float)sy));
		}
	}

	if(sameTweenTile >= 0)
	{
		Tile* sameTile = (Tile*)(roc->getChild(sameTweenTile));
		tile->setSameTweenTile(sameTile);
		for(uint32 i=0;i<sameTile->getTweenCount();++i)
		{
			Tweenable* tweenable = sameTile->getTweenable(i);
			if(tweenable)
				tweenable->addRenderable(tile);
		}
	}
	else
	{
		//read information about tween
		for(TiXmlElement* twn=element->FirstChildElement(_TWEEN_TAG.c_str());twn;twn=twn->NextSiblingElement(_TWEEN_TAG.c_str()))
		{
			TweenReader::parse(twn, tile);
		}
	}

	if(sameAnimationTile >= 0)
		tile->setSameAnimationTile(roc->getChild(sameAnimationTile));
	else
	{
		//read information about animation
		for(TiXmlElement* ani=element->FirstChildElement(_ANIMATION_TAG.c_str());ani;ani=ani->NextSiblingElement(_ANIMATION_TAG.c_str()))
		{
			AnimationReader::parse(ani, tile);
		}
	}

	parseScripts(element, tile);

	return tile;
}

void TileReader::parseScripts(TiXmlElement* element, Tile* tile)
{
	char* scriptFile = (char*)element->Attribute(_SCRIPT_FILE_TAG.c_str());
	TiXmlElement* node = element->FirstChildElement(_SCRIPT_FILE_TAG.c_str()); if(node) scriptFile = (char*)node->GetText();

	if(scriptFile)
	{
#ifdef UNICODE
		TCHAR wstr[512]; memset(wstr, 0, sizeof(wstr));
		::MultiByteToWideChar(CP_UTF8, 0, scriptFile, (int32)strlen(scriptFile), wstr, 512);
		ScriptManager::GetInstance().load(wstr);
#else
		ScriptManager::GetInstance().load(scriptFile);
#endif
	}

	char* onLoad = (char*)element->Attribute(_ON_LOAD_TAG.c_str());
	node = element->FirstChildElement(_ON_LOAD_TAG.c_str()); if(node) onLoad = (char*)node->GetText();

	if(onLoad)
	{
		//call script function
#ifdef UNICODE
		TCHAR wstr[512]; memset(wstr, 0, sizeof(wstr));
		::MultiByteToWideChar(CP_UTF8, 0, onLoad, (int32)strlen(onLoad), wstr, 512);
		SquirrelFunction<void>((const SQChar*)wstr)(tile);
#else
		SquirrelFunction<void>((const SQChar*)onLoad)(tile);
#endif
	}
}

void TileReader::parseAttachment(TiXmlElement* element, Renderable* renderable)
{
	char* sName = (char*)element->Attribute(_NAME_TAG.c_str());
	TiXmlElement* node = element->FirstChildElement(_NAME_TAG.c_str()); if(node) sName = (char*)node->GetText();

	TiXmlElement* position = element->FirstChildElement(_POSITION_TAG.c_str());
	if(position)
	{
		int32 x = 0, y = 0;

		position->Attribute(_X_TAG.c_str(), &x);
		node = position->FirstChildElement(_X_TAG.c_str()); if(node) x = atoi(node->GetText());
		position->Attribute(_Y_TAG.c_str(), &y);
		node = position->FirstChildElement(_Y_TAG.c_str()); if(node) y = atoi(node->GetText());

		if(sName)
#ifdef UNICODE
		{
			TCHAR wstr[512]; memset(wstr, 0, sizeof(wstr));
			::MultiByteToWideChar(CP_UTF8, 0, sName, (int32)strlen(sName), wstr, 512);
			renderable->addAttachment(wstr, Vector2D((float)x, (float)y));
		}
#else
			renderable->addAttachment(sName, Vector2D((float)x, (float)y));
#endif
		else
			renderable->addAttachment(_T(""), Vector2D((float)x, (float)y));
	}
}
